import base64
import json
import random
import sys
import time
import uuid
import mmh3
from pathlib import Path

import psutil
from Cryptodome.Cipher import AES
from loguru import logger

from common import config
from core.website import website


def parse_proxy_config():
    try:
        account_user_id_list = load_account_user_id_file()
        account_device_id_list = load_account_device_id_file()
        proxy_config_list = []
        with open(config.PROXY_CONFIG_FILE_NAME, 'r') as file:
            lines = file.readlines()
            username = None
            password = None
            for line in lines:
                line = line.strip()
                if len(proxy_config_list) >= int(config.GRASS_CLIENT_CONFIG[4]):
                    break
                if ":" in line and "|" not in line:
                    parts = line.split(":")
                    username = parts[0].strip()
                    password = parts[1].strip()
                elif "|" in line:
                    parts = line.split("|")
                    if username and password:
                        fingerprint_key = f"{parts[0]}|{parts[1]}|{parts[2]}|{parts[3]}"
                        fingerprint = json.loads(account_device_id_list.get(fingerprint_key)) if fingerprint_key in account_device_id_list else generate_random_fingerprint()
                        proxy_config = {
                            "sid": generate_sid(username, parts[0].strip(), parts[1].strip()),
                            "user_id": account_user_id_list.get(username),
                            "username": username,
                            "password": password,
                            "device_id": fingerprint["device_id"],
                            "user_agent": fingerprint["user_agent"],
                            "proxy_ip": parts[0].strip(),
                            "proxy_port": parts[1].strip(),
                            "proxy_username": parts[2].strip(),
                            "proxy_password": parts[3].strip()
                        }
                        proxy_config_list.append(proxy_config)
        return proxy_config_list
    except Exception as e:
        logger.error(f"读取系统配置出现异常：{e}")
async def generate_account_file(session_configs):
    try:
        file_path = Path(config.ACCOUNT_USER_ID_FILE_NAME)
        if not file_path.exists():
            file_path.touch()
        with open(config.ACCOUNT_USER_ID_FILE_NAME, 'w') as file:
            user_account_list = set()
            for session_config in session_configs:
                user_account_list.add(f"{session_config['username']}:{session_config['user_id']}\n")
            file.writelines(user_account_list)
    except Exception as e:
        logger.error(f"回写账号user_id配置文件异常：{e}")
async def generate_device_id_file(session_configs):
    try:
        file_path = Path(config.ACCOUNT_DEVICE_ID_FILE_NAME)
        if not file_path.exists():
            file_path.touch()
        with open(config.ACCOUNT_DEVICE_ID_FILE_NAME, 'w') as file:
            user_account_list = set()
            for session_config in session_configs:
                device_key = f"{session_config['proxy_ip']}|{session_config['proxy_port']}|{session_config['proxy_username']}|{session_config['proxy_password']}"
                fingerprint = {"device_id": session_config['device_id'], "user_agent": session_config['user_agent']}
                user_account_list.add(f"{device_key}:{json.dumps(fingerprint)}\n")
            file.writelines(user_account_list)
    except Exception as e:
        logger.error(f"回写账号device_id配置文件异常：{e}")

def load_account_user_id_file():
        user_account_list = {}
        file_path = Path(config.ACCOUNT_USER_ID_FILE_NAME)
        if not file_path.exists():
            file_path.touch()
        with open(config.ACCOUNT_USER_ID_FILE_NAME, 'r') as file:
            lines = file.readlines()
            for line in lines:
                if ":" not in line:
                    continue
                parts = line.split(":")
                user_account_list[parts[0]] = parts[1].lstrip().rstrip()
        return user_account_list

def load_account_device_id_file():
        account_device_id_list = {}
        file_path = Path(config.ACCOUNT_DEVICE_ID_FILE_NAME)
        if not file_path.exists():
            file_path.touch()
        with open(config.ACCOUNT_DEVICE_ID_FILE_NAME, 'r') as file:
            lines = file.readlines()
            for line in lines:
                index = line.find(':')
                if index == -1:
                    continue
                account_device_id_list[line[:index]] = line[index + 1:]
        return account_device_id_list

def generate_sid(username, proxy_ip, proxy_port):
    return f"{username}:{proxy_ip}:{proxy_port}"

def generate_device_id(socks5_proxy):
    return str(uuid.uuid3(uuid.NAMESPACE_DNS, socks5_proxy))

def mark_text_field(text, prefix):
    return text[:prefix] + "x" * 8

def mark_ip_field(ip):
    return '.'.join(ip.split('.')[:-1]) + '.xxx'

def check_is_invalid_proxy_exception(e):
    for ex in config.INVALID_PROXY_EXCEPTION:
        if ex in e.lower():
            return True
    return False

async def init_grass_client_config():
    file_path = Path(config.GRASS_LOCK_FILE_NAME)
    if not file_path.exists():
        file_path.touch()
    with open(config.GRASS_LOCK_FILE_NAME, 'rb') as file:
        decrypted_config = decrypt_string(file.read())
        if decrypted_config is None:
            decrypted_config = f"{config.GEASS_CLIENT_TEST_KEY}||||"
        decrypted_configs = decrypted_config.split("|")
        if len(decrypted_configs) < 5 or len(decrypted_configs[0]) == 0:
            return None
        if len(decrypted_configs[1]) == 0 or len(decrypted_configs[2]) == 0 or len(decrypted_configs[3]) == 0 or len(decrypted_configs[4]) == 0:
            encrypted_content = await website.auth_client(decrypted_configs[0])
            decrypted_config = decrypt_string(encrypted_content)
            if decrypted_config is None:
                return None
            decrypted_configs = decrypted_config.split("|")
            return decrypted_configs
        return decrypted_configs
async def load_grass_client_config():
    try:
        decrypted_configs = await init_grass_client_config()
        if decrypted_configs is None or len(decrypted_configs) < 5:
            return None
        plaintext = f"{'|'.join(decrypted_configs)}|{time.time()}"
        encrypted_content = encrypt_string(plaintext)
        with open(config.GRASS_LOCK_FILE_NAME, 'wb') as file:
            file.write(encrypted_content)
        return decrypted_configs
    except Exception as e:
        logger.error(f"系统异常:{e}")

def get_mac_address():
    return ':'.join('%012x' % uuid.getnode())[-17:].lower()

async def check_is_not_allow_running():
    config.GRASS_CLIENT_CONFIG = await load_grass_client_config()
    if config.GRASS_CLIENT_CONFIG is None:
        logger.error("系统程序异常，无法启动：101")
        sys.exit(0)
    if get_mac_address() != config.GRASS_CLIENT_CONFIG[1]:
        logger.error("系统程序异常，无法启动：102")
        sys.exit(0)
    if int(time.time()) >= int(config.GRASS_CLIENT_CONFIG[2]):
        logger.error("系统程序异常，无法启动，激活码已过期：103")
        sys.exit(0)
    current_process = psutil.Process()
    current_process_name = current_process.name()
    pid_count = 0
    for proc in psutil.process_iter(['name', 'ppid']):
        if proc.info['name'] == current_process_name:
            pid_count = pid_count + 1
    if pid_count > 50:
        logger.error("系统程序异常，无法启动：104")
        sys.exit(0)
    return False

# 填充函数，因为AES加密要求数据长度必须是16的倍数
def pad(s):
    return s + (16 - len(s) % 16) * chr(16 - len(s) % 16)


# 去填充函数
def unpad(s):
    return s[:-ord(s[len(s) - 1:])]


# AES加密函数，加密结果每个字节做取反处理，返回二进制字节数组
def encrypt_string(plaintext):
    if plaintext is None or len(plaintext) == 0:
        return None
    key = config.GRASS_CLIENT_AES_INDEX.encode('utf-8')
    plaintext = pad(plaintext).encode('utf-8')
    cipher = AES.new(key, AES.MODE_ECB)
    encrypted_text = cipher.encrypt(plaintext)
    # 对加密后的每个字节做取反操作
    processed_encrypted_text = bytes([~b & 0xFF for b in base64.b64encode(encrypted_text)])
    return processed_encrypted_text


# AES解密函数，对字节数组做取反操作再进行解密
def decrypt_string(ciphertext):
    if ciphertext is None or len(ciphertext) == 0:
        return None
    key = config.GRASS_CLIENT_AES_INDEX.encode('utf-8')
    # 对传入的字节数组每个字节做取反操作
    processed_ciphertext = bytes([~b & 0xFF for b in ciphertext])
    ciphertext = base64.b64decode(processed_ciphertext)
    cipher = AES.new(key, AES.MODE_ECB)
    decrypted_text = cipher.decrypt(ciphertext)
    return unpad(decrypted_text.decode('utf-8'))

def get_random_macos_version():
    # 主要的 macOS 版本
    versions = [
        {"version": "10_15_7", "name": "Catalina", "weight": 0.3},
        {"version": "10_15_6", "name": "Catalina", "weight": 0.1},
        {"version": "10_14_6", "name": "Mojave", "weight": 0.1},
        {"version": "10_13_6", "name": "High Sierra", "weight": 0.05},
        {"version": "13_6_3", "name": "Ventura", "weight": 0.15},
        {"version": "14_1_1", "name": "Sonoma", "weight": 0.2},
        {"version": "14_1_2", "name": "Sonoma", "weight": 0.1},
    ]

    random_num = random.random()
    current_weight = 0

    for ver in versions:
        current_weight += ver["weight"]
        if random_num <= current_weight:
            return ver["version"]

    return "14_1_2"

def generate_random_fingerprint():
    def get_random_browser_profile():
        chrome_versions = [
            "131.0.6778.86",
            "129.0.6668.101",
            "129.0.6668.71",
            "128.0.6613.138",
            "130.0.6723.70",
        ]

        platforms = {
            "Windows": {
                "name": "Win32",
                "ua": "Windows NT 10.0; Win64; x64",
                "weight": 0.6,
            },
            "Mac": {
                "name": "MacIntel",
                "ua": f"Macintosh; Intel Mac OS X {get_random_macos_version()}",
                "weight": 0.4,
            },
        }

        resolutions = [
            {"res": [1920, 1080], "weight": 0.4},
            {"res": [2560, 1440], "weight": 0.2},
            {"res": [1366, 768], "weight": 0.15},
            {"res": [1440, 900], "weight": 0.15},
            {"res": [3840, 2160], "weight": 0.1},
        ]

        platform = platforms["Windows"] if random.random() < platforms["Windows"]["weight"] else platforms["Mac"]

        # 选择分辨率
        random_res = random.random()
        current_weight = 0
        selected_resolution = resolutions[-1]["res"]  # 默认值
        for res in resolutions:
            current_weight += res["weight"]
            if random_res <= current_weight:
                selected_resolution = res["res"]
                break

        languages = [
            {"lang": "en-US", "weight": 0.4},
            {"lang": "zh-CN", "weight": 0.4},
            {"lang": "en-GB", "weight": 0.1},
            {"lang": "zh-TW", "weight": 0.1},
        ]

        # 选择语言
        random_res = random.random()
        current_weight = 0
        selected_language = languages[0]["lang"]  # 默认值
        for lang in languages:
            current_weight += lang["weight"]
            if random_res <= current_weight:
                selected_language = lang["lang"]
                break

        return {
            "userAgent": f"Mozilla/5.0 ({platform['ua']}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{random.choice(chrome_versions)} Safari/537.36",
            "language": selected_language,
            "platform": platform["name"],
            "colorDepth": 24 if platform["name"] == "Win32" else 30,
            "screenResolution": selected_resolution,
            "hardwareConcurrency": random.choice([4, 6, 8, 12, 16]) if platform["name"] == "Win32" else random.choice(
                [8, 10, 12]),
            "deviceMemory": random.choice([4, 8, 16, 32]) if platform["name"] == "Win32" else random.choice(
                [8, 16, 32]),
            "timezoneOffset": random.choice([480, -420, -480]),
            "hasCanvas": True,
            "hasWebGL": True,
            "hasCookies": True,
            "hasLocalStorage": True,
            "hasSessionStorage": True,
            "pluginsCount": 2 + random.randint(0, 3),
        }

    profile = get_random_browser_profile()
    components = "###".join([
        str(x) for x in [
            profile["userAgent"],
            profile["language"],
            profile["colorDepth"],
            "x".join(map(str, profile["screenResolution"])),
            profile["timezoneOffset"],
            profile["platform"],
            profile["hardwareConcurrency"],
            profile["deviceMemory"],
            profile["pluginsCount"],
            profile["hasCanvas"],
            profile["hasWebGL"],
            profile["hasCookies"],
            profile["hasLocalStorage"],
            profile["hasSessionStorage"],
        ]
    ])

    # 生成 MurmurHash
    hash_value = mmh3.hash(components, 31)
    fingerprint_hash = str(100000000 + (abs(hash_value) % 900000000))
    return {
        "device_id": str(uuid.uuid5(uuid.UUID("bed9e870-4e94-4260-a1fa-815514adfce1"), fingerprint_hash)),
        "user_agent": profile["userAgent"]
    }